package com.bestcem.xm.qdes.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bestcem.bp.xm.user.grpc.v1.services.User;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.exception.BusinessException;
import com.bestcem.xm.common.core.oss.enums.OssPathEnum;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.component.mybatis.service.impl.XmServiceImpl;
import com.bestcem.xm.component.security.dto.TokenDTO;
import com.bestcem.xm.componet.filestorage.service.StorageService;
import com.bestcem.xm.componet.redis.service.RedisService;
import com.bestcem.xm.qdes.config.StaticFile;
import com.bestcem.xm.qdes.controller.param.*;
import com.bestcem.xm.qdes.controller.vo.*;
import com.bestcem.xm.qdes.convert.QdesLabelConvertMapper;
import com.bestcem.xm.qdes.entity.*;
import com.bestcem.xm.qdes.enums.*;
import com.bestcem.xm.qdes.grpc.client.SurveyGrpcClient;
import com.bestcem.xm.qdes.grpc.client.UserGrpcClient;
import com.bestcem.xm.qdes.grpc.client.dto.SurveyRespondentDto;
import com.bestcem.xm.qdes.grpc.client.dto.UserDto;
import com.bestcem.xm.qdes.grpc.v1.services.ProjectEntryStatus;
import com.bestcem.xm.qdes.mapper.*;
import com.bestcem.xm.qdes.mq.dto.CreateProjectDto;
import com.bestcem.xm.qdes.mq.dto.ProjectPermissionChangedDto;
import com.bestcem.xm.qdes.mq.send.biz.QdesMessageSendService;
import com.bestcem.xm.qdes.service.QdesLibProjectService;
import com.bestcem.xm.qdes.service.QdesLocalCacheService;
import com.bestcem.xm.qdes.service.QdesQuestionService;
import com.bestcem.xm.qdes.service.ServiceSequenceService;
import com.bestcem.xm.qdes.util.PageUtil;
import com.bestcem.xm.qdes.util.QdesConstant;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.bestcem.xm.qdes.enums.LibProjectLabelTypeEnum.LIBPROJECTLABEL_LAB_TYPE_INDUSTRY;
import static com.bestcem.xm.qdes.enums.LibProjectLabelTypeEnum.LIBPROJECTLABEL_LAB_TYPE_SCENE;
import static com.bestcem.xm.qdes.enums.LibProjectStatusEnum.LIBPROJECT_STATUS_ENABLED;
import static com.bestcem.xm.qdes.enums.ProjectBadgeStatusEnum.PROJECTENTRY_BADGE_STATUS_READ;
import static com.bestcem.xm.qdes.enums.ProjectImportTypeEnum.*;
import static com.bestcem.xm.qdes.grpc.v1.services.ProjectEntryCategory.PROJECTENTRY_CATEGORY_MANAGER;
import static com.bestcem.xm.qdes.grpc.v1.services.ProjectEntryPermType.PROJECTENTRY_PERM_TYPE_CUSTOM;
import static com.bestcem.xm.qdes.grpc.v1.services.ProjectPtype.PROJECT_PTYPE_SURVEY;
import static com.bestcem.xm.qdes.grpc.v1.services.ProjectPtype.PROJECT_PTYPE_TEMPLET;
import static com.bestcem.xm.qdes.grpc.v1.services.ProjectStatus.PROJECT_STATUS_CREATED;
import static com.bestcem.xm.qdes.grpc.v1.services.QStructStatus.QSTRUCT_STATUS_DRAFT;
import static com.bestcem.xm.qdes.grpc.v1.services.TagRefRefType.TAGREF_REF_TYPE_PROJECT;
import static com.bestcem.xm.survey.grpc.v1.services.RespondentDataStatus.RESPONDENT_DATA_STATUS_FORMAL;
import static com.bestcem.xm.survey.grpc.v1.services.RespondentStatus.RESPONDENT_STATUS_FINISH;

/**
 * @author guitao.lu
 * @version V1.0
 * @date 2022/8/1
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class QdesLibProjectServiceImpl extends XmServiceImpl<QdesLibProjectMapper, QdesLibProject> implements QdesLibProjectService {

    private static final String CUSTOM_ATTR_FILE_PATH = "file/custom_attr.json";
    private static final int MAX_LENGTH = 30;
    private static final String TITLE_REGEX = "[/:*\"<>|?\\\\]";
    private static final String OPEN_GROUPING = "openGrouping";
    private static final String UPDATED_DT = "updatedDT";
    private static final String PAGE_HEADER = "page_header";
    private static final String PHONE_PAGE_HEADER = "phone_page_header";
    private static final String Q_NUM = "Q";
    private static final String G_NUM = "G";
    private static final String ITEMS = "items";
    private static final String COPY_NAME_LIST_TYPE_KEY = "QDES:%s:%d:type";
    private static final String COPY_NAME_LIST_DATA_KEY = "QDES:%s:%d:%d:data";

    private @NonNull QdesTagRefMapper qdesTagrefMapper;
    private @NonNull QdesLibTopicMapper qdesLibtopicMapper;
    private @NonNull QdesLibProjectLabelMapper qdesLibprojectlabelMapper;
    private @NonNull QdesLabelConvertMapper qdesLabelConvertMapper;
    private @NonNull UserGrpcClient userGrpcClient;
    private @NonNull QdesLibProjectLabelRefMapper projectLabelRefMapper;
    private @NonNull QdesQuestionMapper qdesQuestionMapper;
    private @NonNull QdesProjectMapper qdesProjectMapper;
    private @NonNull QdesProjectSettingMapper qdesProjectsettingMapper;
    private @NonNull QdesQstructMapper qdesQstructMapper;
    private @NonNull QdesNamelistMapper qdesNamelistMapper;
    private @NonNull QdesProjectEntryMapper qdesProjectentryMapper;
    private @NonNull UploadFileMapMapper uploadFileMapMapper;
    private @NonNull UploadFileInfoMapper uploadFileInfoMapper;
    private @NonNull QdesQuotaMapper qdesQuotaMapper;

    private @NonNull ServiceSequenceService serviceSequenceService;
    private @NonNull QdesQuestionService questionService;
    //private @NonNull OssService ossService;
    private @NonNull StorageService storageService;
    private @NonNull RedisService redisService;
    private @NonNull SurveyGrpcClient surveyGrpcClient;

    private @NonNull StaticFile staticFile;

    private @NonNull QdesLocalCacheService<String, Object> qdesLocalCacheService;
    private @NonNull QdesMessageSendService qdesMessageSendService;

    private static final String ROOT_FILE_PATH = System.getProperty("user.dir");

    @Override
    public Page<QdesLibProjectVo> pageLibproject(Page<?> page, ProjectLibPageParam param) {
        log.info("Now to pageLibproject, param = {}", param);
        // 修改排序
        if (StringUtils.isEmpty(param.getSortBy())) {
            param.setSortBy(ProjectSortFieldEnum.UPDATED_DT.getValue());
        }
        if (StringUtils.isNotEmpty(param.getLibType())) {
            try {
                Integer.parseInt(param.getLibType());
            } catch (NumberFormatException e) {
                log.error("The libType is error, libType = {}", param.getLibType());
                throw new BusinessException("lib_type必须为整数，请检查。");
            }
        }
        if (param.getDescending() == null) {
            param.setDescending(true);
        }
        List<String> values = Arrays.stream(ProjectSortFieldEnum.values()).map(ProjectSortFieldEnum::getValue)
                .collect(Collectors.toList());
        if (!values.contains(param.getSortBy())) {
            log.error("The sortBy is error, sortBy = {}", param.getSortBy());
            throw new BusinessException("排序字段sortBy不正确，请检查。");
        }
        // 公共库查询
        if (param.getIsOfficial()) {
            return getOfficialPage(page, param);
        }
        // 企业库查询
        Page<String> pages;
        if (StringUtils.isNotEmpty(param.getLibType())) {
            pages = baseMapper.pageLibprojectByLibType(page, param);
        } else if (CollectionUtils.isNotEmpty(param.getLabelIds())) {
            pages = baseMapper.pageLibprojectByTags(page, param);
        } else {
            pages = baseMapper.pageLibproject(page, param);
        }
        List<String> ids = pages.getRecords();
        List<QdesLibProjectVo> records = new ArrayList<>();
        if (!CollectionUtils.isEmpty(ids)) {
            records = baseMapper.pageLibprojectByIds(ids, param);
            // 查询收藏的项目，添加到对应的项目中like里
            LambdaQueryWrapper<QdesLibTopic> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(QdesLibTopic::getCompanyId, param.getOrgId())
                    .eq(QdesLibTopic::getLibType, LibTopicTypeEnum.LIBTOPIC_LIB_TYPE_FAVORITE.getValue())
                    .eq(QdesLibTopic::getIsOfficial, false).last("limit 1");
            QdesLibTopic libtopic = qdesLibtopicMapper.selectOne(queryWrapper);
            String idString = libtopic == null ? null : libtopic.getLibIds();
            if (StringUtils.isNotEmpty(idString)) {
                List<String> idList = JSONArray.parseArray(idString, String.class);
                records.forEach(libProject -> libProject.setLike(idList.contains(libProject.getId())));
            } else {
                records.forEach(libProject -> libProject.setLike(false));
            }
        }
        Page<QdesLibProjectVo> retPage = new Page<>();
        retPage.setRecords(records);
        retPage.setTotal(pages.getTotal());
        retPage.setCurrent(pages.getCurrent());
        return retPage;
    }

    @Override
    public List<QdesLibProjectVo> getAllIndustryProject(ProjectLibPageParam param) {
        log.info("Now to getAllIndustryProject, param = {}", param);
        // 修改排序
        if (StringUtils.isEmpty(param.getSortBy())) {
            param.setSortBy(ProjectSortFieldEnum.UPDATED_DT.getValue());
        }
        if (param.getDescending() == null) {
            param.setDescending(true);
        }
        List<String> values = Arrays.stream(ProjectSortFieldEnum.values()).map(ProjectSortFieldEnum::getValue)
                .collect(Collectors.toList());
        if (!values.contains(param.getSortBy())) {
            log.error("The sortBy is error, sortBy = {}", param.getSortBy());
            throw new BusinessException("排序字段sortBy不正确，请检查。");
        }
        List<QdesLibProjectVo> libprojectVos = baseMapper.getAllIndustryProject(param);
        if (!CollectionUtils.isEmpty(libprojectVos)) {
            // 查询收藏的项目，添加到对应的项目中like里
            LambdaQueryWrapper<QdesLibTopic> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(QdesLibTopic::getCompanyId, param.getOrgId())
                    .eq(QdesLibTopic::getLibType, LibTopicTypeEnum.LIBTOPIC_LIB_TYPE_FAVORITE.getValue())
                    .eq(QdesLibTopic::getIsOfficial, false).last("limit 1");
            QdesLibTopic libtopic = qdesLibtopicMapper.selectOne(queryWrapper);
            String idString = libtopic == null ? null : libtopic.getLibIds();
            if (StringUtils.isNotEmpty(idString)) {
                List<String> ids = JSONArray.parseArray(idString, String.class);
                libprojectVos.forEach(libProject -> libProject.setLike(ids.contains(libProject.getId())));
            } else {
                libprojectVos.forEach(libProject -> libProject.setLike(false));
            }
        }
        return libprojectVos;
    }

    @Override
    public QdesLibProjectVo getOneIndustryById(String id, String orgId) {
        log.info("Now to getOneIndustryById, id = {}", id);
        if (StringUtils.isEmpty(id)) {
            log.error("The getOneIndustryById is error, id = {}", id);
            throw new BusinessException("id为空，请检查。");
        }
        QdesLibProjectVo qdesLibprojectVo = baseMapper.getOneProjectById(id);
        if (qdesLibprojectVo != null) {
            // 查询收藏的项目，添加到对应的项目中like里
            LambdaQueryWrapper<QdesLibTopic> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(QdesLibTopic::getCompanyId, orgId)
                    .eq(QdesLibTopic::getLibType, LibTopicTypeEnum.LIBTOPIC_LIB_TYPE_FAVORITE.getValue())
                    .eq(QdesLibTopic::getIsOfficial, false).last("limit 1");
            QdesLibTopic libtopic = qdesLibtopicMapper.selectOne(queryWrapper);
            String idString = libtopic.getLibIds();
            if (StringUtils.isNotEmpty(idString)) {
                List<String> ids = JSONArray.parseArray(idString, String.class);
                qdesLibprojectVo.setLike(ids.contains(qdesLibprojectVo.getId()));
            } else {
                qdesLibprojectVo.setLike(false);
            }
        }
        return qdesLibprojectVo;
    }

    private Page<QdesLibProjectVo> getOfficialPage(Page<?> page, ProjectLibPageParam param) {
        Page<QdesLibProjectVo> pages = new Page<>();
        // 查询收藏的项目
        LambdaQueryWrapper<QdesLibTopic> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(QdesLibTopic::getCompanyId, param.getOrgId())
                .eq(QdesLibTopic::getLibType, LibTopicTypeEnum.LIBTOPIC_LIB_TYPE_FAVORITE.getValue())
                .eq(QdesLibTopic::getIsOfficial, true).last("limit 1");
        QdesLibTopic libtopic = qdesLibtopicMapper.selectOne(queryWrapper);
        String idString = libtopic == null ? "" : libtopic.getLibIds();
        List<String> likeIds = new ArrayList<>();
        if (StringUtils.isNotEmpty(idString)) {
            likeIds = JSONArray.parseArray(idString, String.class);
        }
        // 根据条件获取需要查询的id集合
        List<String> searchIds = null;
        // 判断是否libType不为空，部位空则获取对应的问卷id集合
        if (StringUtils.isNotEmpty(param.getLibType())) {
            if (LibTopicTypeEnum.LIBTOPIC_LIB_TYPE_FAVORITE.getValue() == Integer.parseInt(param.getLibType())) {
                searchIds = likeIds;
            } else {
                LambdaQueryWrapper<QdesLibTopic> recommentWrapper = Wrappers.lambdaQuery();
                recommentWrapper.eq(QdesLibTopic::getLibType, LibTopicTypeEnum.LIBTOPIC_LIB_TYPE_RECOMMAND.getValue())
                        .eq(QdesLibTopic::getIsOfficial, true).last("limit 1");
                QdesLibTopic recommenLibtopic = qdesLibtopicMapper.selectOne(recommentWrapper);
                String rIdString = recommenLibtopic == null ? "" : recommenLibtopic.getLibIds();
                if (StringUtils.isNotEmpty(rIdString)) {
                    searchIds = JSONArray.parseArray(rIdString, String.class);
                }
            }
            // 如果问卷id集合为空，表示没有对应的数据，直接返回
            if (CollectionUtils.isEmpty(searchIds)) {
                return pages;
            }
        }
        // 判断是否根据标签查询
        if (CollectionUtils.isNotEmpty(param.getLabelIds())) {
            List<String> labelProjectIds = baseMapper.getLibOfficialLabel(param.getLabelIds());
            // 如果根据标签获取id集合为空，表示没有对应的数据，直接返回
            if (CollectionUtils.isEmpty(labelProjectIds)) {
                return pages;
            }
            // 判断searchIds是否为空， 不为空则取交集
            if (CollectionUtils.isEmpty(searchIds)) {
                searchIds = labelProjectIds;
            } else {
                searchIds = (List<String>) CollectionUtils.intersection(searchIds, labelProjectIds);
            }
            // 如果交集为空，表示没有对应的数据，直接返回
            if (CollectionUtils.isEmpty(searchIds)) {
                return pages;
            }
        }
        if (!CollectionUtils.isEmpty(searchIds)) {
            param.setIds(searchIds);
        }
        Page<String> idpages = baseMapper.pageOfficialLibproject(page, param);
        List<String> ids = idpages.getRecords();
        List<QdesLibProjectVo> records = ListUtil.list(false);
        if (!CollectionUtils.isEmpty(ids)) {
            records = baseMapper.getOfficialByIds(ids, param);
            // 查询收藏的项目，添加到对应的项目中like里
            if (!CollectionUtils.isEmpty(likeIds)) {
                for (QdesLibProjectVo libProject : records) {
                    libProject.setLike(likeIds.contains(libProject.getId()));
                }
            } else {
                records.forEach(libProject -> libProject.setLike(false));
            }
        }
        pages.setRecords(records);
        return pages;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OnlyIdVo createLibProject(ProjectLibCreateParam param) throws IOException {
        log.info("Now to createLibProject, param = {}", param);
        checkHasTitle(param.getTitle(), param.getOrgId(), LIBPROJECT_STATUS_ENABLED.getValue(), null);
        checkTag(param.getLabels());
        // 创建问卷项目
        CopyProjectParam copyProjectParam = new CopyProjectParam();
        copyProjectParam.setPid(param.getPid());
        copyProjectParam.setTitle(param.getTitle());
        copyProjectParam.setCompanyId(param.getOrgId());
        copyProjectParam.setUserId(param.getUserId());
        copyProjectParam.setTempletNotSetQstructTitle(true);
        copyProjectParam.setLibproject(1);
        copyProjectParam.setCopyType(PROJECT_CREAT_LIB.getValue());
        copyProjectParam.setNeedCheck(false);
        copyProjectParam.setPType(PROJECT_PTYPE_TEMPLET.getNumber());
        copyProjectParam.setForTemplate(true);
        QdesProject project = copyProject(copyProjectParam);
        // 获取数量
        LambdaQueryWrapper<QdesQuestion> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(QdesQuestion::getStructId, project.getStructId());
        Long count = qdesQuestionMapper.selectCount(queryWrapper);
        // 创建libproject
        QdesLibProject libproject = new QdesLibProject();
        libproject.setProjectId(project.getId());
        libproject.setIsOfficial(param.getIsOfficial());
        libproject.setStatus(LIBPROJECT_STATUS_ENABLED.getValue());
        libproject.setCompanyId(param.getOrgId());
        libproject.setPtype(TagRefTypeEnum.TEMPLATE.getValue());
        libproject.setTitle(param.getTitle());
        libproject.setCreator(param.getUserId());
        libproject.setQuestionCount(count.intValue());
        baseMapper.insert(libproject);
        // 如果有labels传入，则直接写入关联记录
        if (!CollectionUtils.isEmpty(param.getLabels())) {
            if (param.getIsOfficial()) {
                // 公共的是放在ds_libproject_libprojectlabel_ref表中
                List<QdesLibProjectLabelRef> refs = new ArrayList<>();
                param.getLabels().forEach(label -> {
                    QdesLibProjectLabelRef tagref = new QdesLibProjectLabelRef();
                    tagref.setLibprojectId(libproject.getProjectId());
                    tagref.setLibprojectlabelId(label.getTagId());
                    refs.add(tagref);
                });
                projectLabelRefMapper.insertBatchSomeColumn(refs);
            } else {
                List<QdesTagRef> tagrefList = new ArrayList<>();
                param.getLabels().forEach(label -> {
                    QdesTagRef tagref = new QdesTagRef();
                    tagref.setProjectId(libproject.getProjectId());
                    tagref.setCompanyId(libproject.getCompanyId());
                    tagref.setTagId(label.getTagId());
                    tagref.setTagTitle(label.getTagTitle());
                    tagref.setIsCopyDemo(false);
                    tagref.setRefType(TagRefTypeEnum.TEMPLATE.getValue());
                    tagref.setRefId(libproject.getId());
                    tagref.setCreator(libproject.getCreator());
                    tagrefList.add(tagref);
                });
                qdesTagrefMapper.insertBatchSomeColumn(tagrefList);
            }
        } else {
            LambdaQueryWrapper<QdesTagRef> tagQuery = Wrappers.lambdaQuery();
            tagQuery.eq(QdesTagRef::getRefId, copyProjectParam.getPid());
            List<QdesTagRef> tagrefs = qdesTagrefMapper.selectList(tagQuery);
            List<QdesTagRef> tagrefList = new ArrayList<>();
            if (!CollectionUtils.isEmpty(tagrefs)) {
                tagrefs.forEach(label -> {
                    QdesTagRef tagref = new QdesTagRef();
                    tagref.setProjectId(project.getId());
                    tagref.setCompanyId(libproject.getCompanyId());
                    tagref.setTagId(label.getTagId());
                    tagref.setTagTitle(label.getTagTitle());
                    tagref.setIsCopyDemo(false);
                    tagref.setRefType(TagRefTypeEnum.PROJECT.getValue());
                    tagref.setRefId(libproject.getId());
                    tagref.setCreator(libproject.getCreator());
                    tagrefList.add(tagref);
                });
                qdesTagrefMapper.insertBatchSomeColumn(tagrefList);
            }
        }
        // 创建模版库后台生成缩略图
        genTempletThumbnail(libproject.getId());
        return new OnlyIdVo(libproject.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OnlyIdVo updateLibProject(ProjectLibUpdateParam param, String id, TokenDTO tokenDTO) {
        log.info("Now to updateLibProject, param = {}", param);
        QdesLibProject oldProject = baseMapper.selectById(id);
        if (null == oldProject) {
            log.error("The project is null, id = {}", id);
            throw new BusinessException("根据id无法找到数据");
        }
        if (null != tokenDTO) {
            if (!oldProject.getCreator().equals(tokenDTO.getUserId())
                    && tokenDTO.getIsSenior() == 0
                    && tokenDTO.getSuperRole() == 0) {
                log.error("You have no permission, id = {}", id);
                throw new BusinessException("没有权限修改该模板");
            }
        }
        // 修改标签
        if (param.getLabels() != null) {
            checkTag(param.getLabels());
            if (param.getIsOfficial()) {
                updateOfficialLabels(id, param);
            } else {
                updateIndustryLabels(id, param, oldProject);
            }
            // 修改收藏，推荐
        } else if (param.getLibType() != null) {
            LambdaQueryWrapper<QdesLibTopic> queryWrapper = Wrappers.lambdaQuery();
            //构造查询条件
            queryWrapper.eq(QdesLibTopic::getCompanyId, param.getOrgId()).eq(QdesLibTopic::getLibType, param.getLibType())
                    .eq(QdesLibTopic::getIsOfficial, param.getIsOfficial()).last("limit 1");
            QdesLibTopic qdesLibtopic = qdesLibtopicMapper.selectOne(queryWrapper);
            if (qdesLibtopic == null) {
                if (param.getLike()) {
                    qdesLibtopic = new QdesLibTopic();
                    qdesLibtopic.setCreator(param.getUserId());
                    qdesLibtopic.setCompanyId(param.getOrgId());
                    qdesLibtopic.setLibType(param.getLibType());
                    qdesLibtopic.setPtype(TagRefTypeEnum.TEMPLATE.getValue());
                    qdesLibtopic.setIsOfficial(param.getIsOfficial());
                    List<String> ids = new ArrayList<>();
                    ids.add(id);
                    qdesLibtopic.setLibIds(JSONArray.toJSONString(ids));
                    qdesLibtopicMapper.insert(qdesLibtopic);
                }
            } else {
                List<String> ids = new ArrayList<>();
                String idString = qdesLibtopic.getLibIds();
                if (StringUtils.isNotEmpty(idString)) {
                    ids = JSONArray.parseArray(idString, String.class);
                    if (param.getLike()) {
                        if (ids != null && !ids.contains(id)) {
                            ids.add(id);
                        }
                    } else {
                        if (ids != null) {
                            ids.remove(id);
                        }
                    }
                } else {
                    if (param.getLike()) {
                        ids.add(id);
                    }
                }
                qdesLibtopic.setLibIds(JSONArray.toJSONString(ids));
                qdesLibtopicMapper.updateById(qdesLibtopic);
            }
            // 修改标题头
        } else {
            checkHasTitle(param.getTitle(), param.getOrgId(), LIBPROJECT_STATUS_ENABLED.getValue(), id);
            oldProject.setTitle(param.getTitle());
            baseMapper.updateById(oldProject);
        }
        return new OnlyIdVo(id);
    }

    private void updateOfficialLabels(String id, ProjectLibUpdateParam param) {
        LambdaQueryWrapper<QdesLibProjectLabelRef> queryWrapper = Wrappers.lambdaQuery();
        //构造查询条件
        queryWrapper.eq(QdesLibProjectLabelRef::getLibprojectId, id);
        List<QdesLibProjectLabelRef> qdesTagrefs = projectLabelRefMapper.selectList(queryWrapper);
        List<QdesLibProjectLabelRef> insertTagrefs = new ArrayList<>();
        List<String> deleteIds = new ArrayList<>();
        if (CollectionUtils.isEmpty(qdesTagrefs)) {
            param.getLabels().forEach(oneLabel -> {
                QdesLibProjectLabelRef qdesTagref = new QdesLibProjectLabelRef();
                qdesTagref.setLibprojectId(id);
                qdesTagref.setLibprojectlabelId(oneLabel.getTagId());
                insertTagrefs.add(qdesTagref);
            });
        } else {
            List<String> oldLabelIds = qdesTagrefs.stream().map(QdesLibProjectLabelRef::getLibprojectlabelId).collect(Collectors.toList());
            List<String> newLabelIds = param.getLabels().stream().map(TagLibParam::getTagId).collect(Collectors.toList());
            for (TagLibParam tagref : param.getLabels()) {
                if (!oldLabelIds.contains(tagref.getTagId())) {
                    QdesLibProjectLabelRef qdesTagref = new QdesLibProjectLabelRef();
                    qdesTagref.setLibprojectId(id);
                    qdesTagref.setLibprojectlabelId(tagref.getTagId());
                    insertTagrefs.add(qdesTagref);
                }
            }
            deleteIds = (List<String>) CollectionUtils.subtract(oldLabelIds, newLabelIds);
        }
        if (!CollectionUtils.isEmpty(insertTagrefs)) {
            projectLabelRefMapper.insertBatchSomeColumn(insertTagrefs);
        }
        if (!CollectionUtils.isEmpty(deleteIds)) {
            LambdaQueryWrapper<QdesLibProjectLabelRef> deleteWrapper = Wrappers.lambdaQuery();
            deleteWrapper.in(QdesLibProjectLabelRef::getLibprojectlabelId, deleteIds)
                    .eq(QdesLibProjectLabelRef::getLibprojectId, id);
            projectLabelRefMapper.delete(deleteWrapper);
        }
    }

    private void updateIndustryLabels(String id, ProjectLibUpdateParam param, QdesLibProject oldProject) {
        LambdaQueryWrapper<QdesTagRef> queryWrapper = Wrappers.lambdaQuery();
        //构造查询条件
        queryWrapper.eq(QdesTagRef::getRefId, id);
        List<QdesTagRef> qdesTagRefs = qdesTagrefMapper.selectList(queryWrapper);
        List<QdesTagRef> insertTagrefs = new ArrayList<>();
        List<String> deleteIds = new ArrayList<>();
        if (CollectionUtils.isEmpty(qdesTagRefs)) {
            param.getLabels().forEach(oneLabel -> {
                QdesTagRef qdesTagref = new QdesTagRef();
                qdesTagref.setTagId(oneLabel.getTagId());
                qdesTagref.setRefId(id);
                qdesTagref.setTagTitle(oneLabel.getTagTitle());
                qdesTagref.setRefType(TagRefTypeEnum.TEMPLATE.getValue());
                qdesTagref.setProjectId(oldProject.getProjectId());
                qdesTagref.setCreator(param.getUserId());
                qdesTagref.setCompanyId(param.getOrgId());
                qdesTagref.setIsCopyDemo(false);
                insertTagrefs.add(qdesTagref);
            });
        } else {
            List<String> oldLabelIds = qdesTagRefs.stream().map(QdesTagRef::getTagId).collect(Collectors.toList());
            List<String> newLabelIds = CollectionUtils.isEmpty(param.getLabels()) ? new ArrayList<>() : param
                    .getLabels().stream().map(TagLibParam::getTagId).collect(Collectors.toList());
            for (TagLibParam tagref : param.getLabels()) {
                if (!oldLabelIds.contains(tagref.getTagId())) {
                    QdesTagRef qdesTagref = new QdesTagRef();
                    qdesTagref.setTagId(tagref.getTagId());
                    qdesTagref.setRefId(id);
                    qdesTagref.setTagTitle(tagref.getTagTitle());
                    qdesTagref.setRefType(TagRefTypeEnum.TEMPLATE.getValue());
                    qdesTagref.setProjectId(oldProject.getProjectId());
                    qdesTagref.setCreator(param.getUserId());
                    qdesTagref.setCompanyId(param.getOrgId());
                    qdesTagref.setIsCopyDemo(false);
                    insertTagrefs.add(qdesTagref);
                }
            }
            deleteIds = (List<String>) CollectionUtils.subtract(oldLabelIds, newLabelIds);
        }
        if (!CollectionUtils.isEmpty(insertTagrefs)) {
            qdesTagrefMapper.insertBatchSomeColumn(insertTagrefs);
        }
        if (!CollectionUtils.isEmpty(deleteIds)) {
            LambdaQueryWrapper<QdesTagRef> deleteWrapper = Wrappers.lambdaQuery();
            deleteWrapper.in(QdesTagRef::getTagId, deleteIds).eq(QdesTagRef::getRefId, id);
            qdesTagrefMapper.delete(deleteWrapper);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteLibProject(String id, TokenDTO tokenDTO) {
        log.info("Now to deleteLibProject, id = {}", id);
        QdesLibProject oldProject = baseMapper.selectById(id);
        if (null == oldProject) {
            log.error("The project is null, id = {}", id);
            throw new BusinessException("根据id无法找到数据");
        }
        if (null != tokenDTO) {
            if (!oldProject.getCreator().equals(tokenDTO.getUserId())
                    && tokenDTO.getIsSenior() == 0
                    && tokenDTO.getSuperRole() == 0) {
                log.error("You have no permission, id = {}", id);
                throw new BusinessException("没有权限删除该模板");
            }
        }
        baseMapper.deleteById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OnlyIdVo duplicateProjectLib(ProjectLibDuplicateParam param) {
        log.info("Now to DuplicateProjectLib, id = {}", param);
        QdesLibProject oldProject = baseMapper.selectById(param.getId());
        if (oldProject == null) {
            log.error("Error to DuplicateProjectLib, param = {}", param);
            throw new BusinessException("根据id无法找到数据");
        }
        checkHasTitle(param.getTitle(), param.getOrgId(), LIBPROJECT_STATUS_ENABLED.getValue(), null);
        checkTag(param.getLabels());
        QdesLibProject project = new QdesLibProject();
        project.setProjectId(oldProject.getProjectId());
        project.setIsOfficial(oldProject.getIsOfficial());
        project.setStatus(LIBPROJECT_STATUS_ENABLED.getValue());
        project.setCompanyId(param.getOrgId());
        project.setPtype(oldProject.getPtype());
        project.setTitle(param.getTitle());
        project.setCreator(param.getUserId());
        project.setThumbnail(oldProject.getThumbnail());
        baseMapper.insert(project);

        if (!CollectionUtils.isEmpty(param.getLabels())) {
            List<QdesTagRef> tagrefList = new ArrayList<>();
            param.getLabels().forEach(label -> {
                QdesTagRef tagref = new QdesTagRef();
                tagref.setProjectId(project.getProjectId());
                tagref.setCompanyId(project.getCompanyId());
                tagref.setTagId(label.getTagId());
                tagref.setTagTitle(label.getTagTitle());
                tagref.setIsCopyDemo(false);
                tagref.setRefType(TagRefTypeEnum.TEMPLATE.getValue());
                tagref.setRefId(project.getId());
                tagref.setCreator(project.getCreator());
                tagrefList.add(tagref);
            });
            qdesTagrefMapper.insertBatchSomeColumn(tagrefList);
        }
        return new OnlyIdVo(project.getId());
    }

    @Override
    public List<QdesLibprojectUserVo> getProjectLibUser(String orgId) {
        if (StringUtils.isEmpty(orgId)) {
            log.error("The orgId is null");
            throw new BusinessException("未获取到用户组织");
        }
        List<QdesLibprojectUserVo> users = new ArrayList<>();
        List<String> userIds = baseMapper.getLibProjectUser(orgId);
        if (CollectionUtils.isEmpty(userIds)) {
            return users;
        }
        List<UserDto> userV1s = userGrpcClient.listUserByUserIds(userIds);
        if (!CollectionUtils.isEmpty(userV1s)) {
            userV1s.forEach(userV1 -> {
                QdesLibprojectUserVo userVo = new QdesLibprojectUserVo();
                userVo.setId(userV1.getId());
                userVo.setName(userV1.getName());
                users.add(userVo);
            });
        }
        return users;
    }

    @Override
    public QdesLibprojectLabelListVo getProjectLibLabel() {
        LambdaQueryWrapper<QdesLibProjectLabel> queryWrapper = Wrappers.lambdaQuery();
        //构造查询条件
        List<Integer> types = new ArrayList<>();
        types.add(LIBPROJECTLABEL_LAB_TYPE_SCENE.getValue());
        types.add(LIBPROJECTLABEL_LAB_TYPE_INDUSTRY.getValue());
        queryWrapper.eq(QdesLibProjectLabel::getIsOfficial, true).eq(QdesLibProjectLabel::getDeleted, LIBPROJECT_STATUS_ENABLED.getValue())
                .eq(QdesLibProjectLabel::getStatus, LIBPROJECT_STATUS_ENABLED.getValue()).in(QdesLibProjectLabel::getLabType, types)
                .orderByAsc(QdesLibProjectLabel::getSortNo);
        List<QdesLibProjectLabel> labels = qdesLibprojectlabelMapper.selectList(queryWrapper);
        QdesLibprojectLabelListVo qdesLibprojectLabelListVo = new QdesLibprojectLabelListVo();
        if (labels == null) {
            return qdesLibprojectLabelListVo;
        }
        // 设置scene和industry
        List<QdesLibprojectLabelVo> labelVos = qdesLabelConvertMapper.labels2Vo(labels);
        List<QdesLibprojectLabelVo> scenes = new ArrayList<>();
        List<QdesLibprojectLabelVo> industries = new ArrayList<>();
        labelVos.forEach(label -> {
            if (label.getLabType() != null && LIBPROJECTLABEL_LAB_TYPE_SCENE.getValue() == label.getLabType()) {
                scenes.add(label);
            } else {
                industries.add(label);
            }
        });
        qdesLibprojectLabelListVo.setIndustry(industries);
        qdesLibprojectLabelListVo.setScene(scenes);
        return qdesLibprojectLabelListVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public QdesProject useProjectTemp(String id, String userId, String orgId, JSONObject param) throws IOException {
        QdesLibProject qdesLibproject = baseMapper.selectById(id);
        if (qdesLibproject == null) {
            log.error("The qdesLibproject is null, id = {}", id);
            throw new BusinessException("模板为空，请检查。");
        }
        String title = Optional.ofNullable(param.getString("title")).orElse(qdesLibproject.getTitle());
        checkTitle(title);
        // 复制项目
        QdesProject project = qdesProjectMapper.selectById(qdesLibproject.getProjectId());
        if (project == null) {
            log.error("The project is null, pid = {}", qdesLibproject.getProjectId());
            throw new BusinessException("项目为空，请检查。");
        }
        // 创建项目
        CopyProjectParam copyProjectParam = new CopyProjectParam();
        copyProjectParam.setPid(project.getId());
        copyProjectParam.setTitle(title);
        copyProjectParam.setCompanyId(orgId);
        copyProjectParam.setUserId(userId);
        copyProjectParam.setTempletNotSetQstructTitle(true);
        copyProjectParam.setTempletAddWj(true);
        copyProjectParam.setLibprojectId(id);
        copyProjectParam.setCopyType(PROJECT_IMPORT.getValue());
        copyProjectParam.setNeedCheck(true);
        copyProjectParam.setFolderId(Optional.ofNullable(param.getString("folder_id")).orElse(""));
        // 没有公共模板，所以不需要修改次数
        return copyProject(copyProjectParam);
    }

    @Override
    public QdesProject copyProject(String id, ProjectCreateParam param) throws IOException {
        // 创建项目
        CopyProjectParam copyProjectParam = new CopyProjectParam();
        copyProjectParam.setPid(id);
        copyProjectParam.setTitle(param.getTitle());
        copyProjectParam.setCompanyId(param.getOrgId());
        copyProjectParam.setUserId(param.getUserId());
        copyProjectParam.setTempletNotSetQstructTitle(false);
        copyProjectParam.setTempletAddWj(false);
        copyProjectParam.setCopyType(PROJECT_COPY.getValue());
        copyProjectParam.setNeedCheck(true);
        copyProjectParam.setFolderId(param.getFolderId());
        QdesProject newProject = copyProject(copyProjectParam);
        // 复制标签
        LambdaQueryWrapper<QdesTagRef> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(QdesTagRef::getProjectId, id);
        List<QdesTagRef> tagrefs = qdesTagrefMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(tagrefs)) {
            List<QdesTagRef> tagrefList = new ArrayList<>();
            tagrefs.forEach(label -> {
                QdesTagRef tagref = new QdesTagRef();
                tagref.setProjectId(newProject.getId());
                tagref.setCompanyId(param.getOrgId());
                tagref.setTagId(label.getTagId());
                tagref.setTagTitle(label.getTagTitle());
                tagref.setIsCopyDemo(false);
                tagref.setRefType(TAGREF_REF_TYPE_PROJECT.getNumber());
                tagref.setRefId(newProject.getId());
                tagref.setCreator(param.getUserId());
                tagrefList.add(tagref);
            });
            qdesTagrefMapper.insertBatchSomeColumn(tagrefList);
        }
        // 没有公共模板，所以不需要修改次数
        return newProject;
    }

    private QdesProject copyProject(CopyProjectParam copyProjectParam) throws IOException {
        QdesProject project = qdesProjectMapper.selectById(copyProjectParam.getPid());
        if (project == null) {
            log.error("The project is null, pid = {}", copyProjectParam.getPid());
            throw new BusinessException("项目为空，请检查。");
        }
        checkQstructQuestionCid(project.getStructId(), copyProjectParam.isNeedCheck());
        // 获取最大的code值
        String code;
        if (StringUtils.isNotEmpty(copyProjectParam.getCompanyId())) {
            code = getCode(copyProjectParam.getCompanyId());
        } else {
            code = getCode(project.getCompanyId());
        }
        // 分组模式下线，复制项目从问卷模板中创建默认生成控件模式
        String customAttr = project.getCustomAttr();
        if (StringUtils.isNotEmpty(customAttr)) {
            JSONObject cusJson = JSONObject.parseObject(customAttr);
            if (cusJson.get(OPEN_GROUPING) != null && cusJson.getInteger(OPEN_GROUPING) == ProjectEnableGroupEnum
                    .ENABLE_OPEN_GROUPING.getValue()) {
                cusJson.put(OPEN_GROUPING, ProjectEnableGroupEnum.DISABLE_OPEN_GROUPING.getValue());
            }
            cusJson.put("copyfrompid", project.getId());
            customAttr = JSONObject.toJSONString(cusJson);
        }
        // 创建项目
        QdesProject newProject = new QdesProject();
        newProject.setCode(code);
        newProject.setTitle(copyProjectParam.getTitle());
        newProject.setCreator(copyProjectParam.getUserId());
        newProject.setCompanyId(copyProjectParam.getCompanyId());
        newProject.setVersion(1);
        newProject.setStructId("");
        newProject.setCustomAttr(customAttr);
        newProject.setStatus(PROJECT_STATUS_CREATED.getNumber());
        if (copyProjectParam.getPType() != null) {
            newProject.setPtype(copyProjectParam.getPType());
        } else {
            newProject.setPtype(PROJECT_PTYPE_SURVEY.getNumber());
        }
        // 数据库中有值
        newProject.setIsCopyDemo(false);
        newProject.setReleaseVer(0);
        newProject.setWorktableInfo("{}");
        //QdesProject proResult = createProject(newProject, copyProjectParam.isForTemplate());
        QdesProject proResult = createOneProject(newProject, copyProjectParam.isForTemplate(), false);
        String qsid = copyStruct(project.getStructId(), newProject.getId(), copyProjectParam.getTitle(),
                copyProjectParam.isTempletNotSetQstructTitle(), null);
        proResult.setStructId(qsid);
        // 将structId更新到project中
        LambdaUpdateWrapper<QdesProject> updateWrapper = new LambdaUpdateWrapper<>();
        //构造查询条件-更新
        updateWrapper.set(QdesProject::getStructId, qsid)
                .eq(QdesProject::getId, proResult.getId())
                .eq(QdesProject::getVersion, 1);
        qdesProjectMapper.update(null, updateWrapper);
        // 拷贝copy_quotas
        if (!copyProjectParam.isForTemplate()) {
            copyQuotas(copyProjectParam.getPid(), proResult.getId());
        }
        if (copyProjectParam.isTempletNotSetQstructTitle() && !copyProjectParam.isTempletAddWj()) {
            log.info("No need to create_projectentry");
        } else {
            User adminUser = userGrpcClient.getAdminUserByOrgId(copyProjectParam.getCompanyId());
            String adminRoleId = adminUser == null ? "" : adminUser.getId();
            List<String> seniorIds = userGrpcClient.getSeniorAdminIdByOrgId(copyProjectParam.getCompanyId());
            List<String> allUsers = new ArrayList<>();
            allUsers.add(copyProjectParam.getUserId());
            if (StringUtils.isNotEmpty(adminRoleId)) {
                allUsers.add(adminRoleId);
            }
            if (CollectionUtils.isNotEmpty(seniorIds)) {
                allUsers.addAll(seniorIds);
            }
            allUsers = allUsers.stream().distinct().collect(Collectors.toList());
            for (String userId : allUsers) {
                // 创建projectentry
                List<Integer> permissions = Arrays.stream(ProjectPermissionEnum.values())
                        .map(ProjectPermissionEnum::getValue).collect(Collectors.toList());
                QdesProjectEntry projectentry = new QdesProjectEntry();
                projectentry.setOrgId(copyProjectParam.getCompanyId());
                projectentry.setProjectId(newProject.getId());
                projectentry.setCategory(PROJECTENTRY_CATEGORY_MANAGER.getNumber());
                projectentry.setPermissionsArry(JSONObject.toJSONString(permissions));
                projectentry.setUserId(userId);
                projectentry.setTitle(copyProjectParam.getTitle());
                projectentry.setStatus(ProjectEntryStatus.PROJECTENTRY_STATUS_CREATED.getNumber());
                projectentry.setPermType(PROJECTENTRY_PERM_TYPE_CUSTOM.getNumber());
                projectentry.setCreatorId(copyProjectParam.getUserId());
                projectentry.setBadgeStatus(PROJECTENTRY_BADGE_STATUS_READ.getValue());
                projectentry.setFolderId(Optional.ofNullable(copyProjectParam.getFolderId()).orElse(""));
                qdesProjectentryMapper.insert(projectentry);
                if (CollectionUtils.isNotEmpty(permissions)) {
                    ProjectPermissionChangedDto permissionChangedDto = new ProjectPermissionChangedDto().setAdd(permissions)
                            .setCreate(true).setDel(new ArrayList<>()).setAction("create");
                    qdesMessageSendService.projectPermissionChangedSend(permissionChangedDto);
                }
            }
        }
        // 复制后的项目分配标签
        if (copyProjectParam.getLibproject() == null) {
            setTagRefs(copyProjectParam.getPid(), newProject, copyProjectParam.getCompanyId(), copyProjectParam.getUserId());
        }
        if (StringUtils.isNotEmpty(copyProjectParam.getLibprojectId())) {
            setTagRefs(copyProjectParam.getLibprojectId(), newProject, copyProjectParam.getCompanyId(), copyProjectParam.getUserId());
        }
        return proResult;
    }

    //private QdesProject createProject(QdesProject project, boolean forTemplet) {
    //    return createOneProject(project, forTemplet, false);
    //}

    private QdesProject createOneProject(QdesProject project, boolean forTemplet, boolean forImport) {
        if (!forTemplet) {
            String creator = project.getCreator();
            List<String> groupIds = userGrpcClient.listAncestorIdsByUserId(creator);
            if (CollectionUtils.isNotEmpty(groupIds)) {
                project.setGroupList(JSONArray.toJSONString(groupIds));
            }
        }
        String orgId = project.getCompanyId();
        String customAttr = project.getCustomAttr();
        if (StringUtils.isEmpty(customAttr)) {
            // 没有指定customAttr的情况需要从全局设置里取配置
            JSONObject jsonObject = getDefaultCustomAttr();
            jsonObject.put("wxShare_title", project.getTitle());
            jsonObject.put("wxCardShare_title", project.getTitle());
            LambdaQueryWrapper<QdesProjectSetting> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(QdesProjectSetting::getOrgId, orgId).last("limit 1");
            QdesProjectSetting projectsetting = qdesProjectsettingMapper.selectOne(queryWrapper);
            if (projectsetting != null) {
                jsonObject.put(OPEN_GROUPING, projectsetting.getOpenGrouping());
                jsonObject.put("openCustomCid", projectsetting.getOpenCustomCid());
            }
            project.setCustomAttr(JSONObject.toJSONString(jsonObject));
        }
        if (StringUtils.isEmpty(project.getGroupList())) {
            project.setGroupList("[]");
        }
        if (StringUtils.isEmpty(project.getWorktableInfo())) {
            project.setWorktableInfo("{}");
        }
        qdesProjectMapper.insert(project);
        if (!forTemplet) {
            CreateProjectDto projectDto = new CreateProjectDto().setId(project.getId()).setOrgId(orgId);
            qdesMessageSendService.qdesProjectCreatedSend(projectDto);
        }
        if (forImport) {
            // 如果是导入的项目 先将其置为deleted状态，以避免无用的导入数据干扰 用数统计和入口生成
            project.setDeleted(true);
        }
        return project;
    }

    /**
     * 复制结构数据
     *
     * @return : 结构数据ID
     * @params sid: 结构数据ID
     * @params version: 复制后的结构数据版本
     * @params new_pid: 复制后的结构数据所在的问卷ID
     * @params new_title: 复制后的问卷标题，如果指定了该参数，说明是复制问卷，原结构版本不变，新结构使用version，否则, 原结构版本号更新为version，复制出来的新结构版本号为原版本号
     */
    @Override
    public String copyStruct(String sid, String new_pid, String new_title, boolean templet_not_set_qstructtitle, Integer version) throws IOException {
        QdesQstruct qstruct = Optional.ofNullable(qdesQstructMapper.selectById(sid)).orElseThrow(() -> new BusinessException(String.format("qstruct[id=%s] not found", sid)));
        String data = qstruct.getData();
        if (Objects.isNull(version)) {
            version = 1;
        }
        Integer newVersion;
        if (StringUtils.isNotEmpty(new_title)) {
            if (!templet_not_set_qstructtitle && StringUtils.isNotEmpty(data)) {
                JSONObject dataJson = JSONObject.parseObject(data);
                JSONObject contentJson = Optional.ofNullable(dataJson)
                        .map(it -> it.getJSONObject("desc_begin_page"))
                        .map(d -> d.getJSONObject("content"))
                        .orElse(new JSONObject());
                contentJson.put("text", new_title);
                data = JSONObject.toJSONString(dataJson);
            }
            newVersion = version;
        } else {
            LambdaUpdateWrapper<QdesQstruct> updateWrapper = new LambdaUpdateWrapper<>();
            //构造查询条件-更新
            updateWrapper.set(QdesQstruct::getVersion, version)
                    .set(QdesQstruct::getUpdatedDt, LocalDateTime.now())
                    .eq(QdesQstruct::getId, qstruct.getId());
            qdesQstructMapper.update(null, updateWrapper);
            newVersion = qstruct.getVersion();
        }

        String projectId = Optional.ofNullable(new_pid).orElse(qstruct.getProjectId());
        QdesQstruct newStruct = new QdesQstruct();
        newStruct.setProjectId(projectId);
        newStruct.setVersion(newVersion);
        newStruct.setGcss(copyQstructFile(qstruct.getGcss()).toJSONString());
        newStruct.setGjs(copyQstructFile(qstruct.getGjs()).toJSONString());
        newStruct.setUcss(genQstructUcss(qstruct.getProjectId(), qstruct));
        newStruct.setTemplates(copyQstructFileList(qstruct.getTemplates()));
        newStruct.setPolicy(copyQstructFileList(qstruct.getPolicy()));
        newStruct.setAttachments(copyQstructFileList(qstruct.getAttachments()));
        newStruct.setScores(qstruct.getScores());
        newStruct.setData(data);
        newStruct.setScoreConfig(qstruct.getScoreConfig());

        QdesQstruct new_qstruct = createQstruct(newStruct);
        String qstruct_id = new_qstruct.getId();
        log.info("copyStruct qstruct_id={},qstruct.id={}", qstruct_id, qstruct.getId());

        Map<Integer, JSONObject> allqs = new HashMap<>();
        //考虑覆盖一个已有的qstruct
        Map<Integer, JSONObject> questions = new HashMap<>();
        Map<Integer, JSONObject> all_q_dict = new HashMap<>();
        Map<Integer, JSONObject> old_questions = new HashMap<>();
        LambdaQueryWrapper<QdesQuestion> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.in(QdesQuestion::getStructId, ListUtil.of(qstruct_id, qstruct.getId()));
        List<QdesQuestion> questionList = qdesQuestionMapper.selectList(queryWrapper);
        questionList.forEach(item -> {
            Integer gid = item.getGid();
            String qsid = item.getStructId();
            if (qsid.equals(qstruct_id)) {
                questions.put(gid, JSONObject.parseObject(JSONObject.toJSONString(item)));
            } else if (qsid.equals(qstruct.getId())) {
                all_q_dict.put(gid, JSONObject.parseObject(JSONObject.toJSONString(item)));
                allqs.put(gid, JSONObject.parseObject(item.getData()));
            }
        });

        JSONArray aqlist = new JSONArray();
        questionService.getQstructQuestions(JSON.parseObject(data), allqs, aqlist);
        aqlist.forEach(sob -> {
            JSONObject q = (JSONObject) sob;
            JSONObject newobj = new JSONObject();
            newobj.put("structID", qstruct_id);
            newobj.put("gid", q.get("gid"));
            newobj.put("data", q);
            old_questions.put(q.getInteger("gid"), newobj);
        });

        List<String> del_id_list = new ArrayList<>();
        List<Integer> dirty_qids;
        if (all_q_dict.keySet().size() != old_questions.keySet().size()) {
            dirty_qids = (List<Integer>) CollectionUtils.subtract(all_q_dict.keySet(), old_questions.keySet());
            if (CollectionUtils.isNotEmpty(dirty_qids)) {
                dirty_qids.forEach(qid -> {
                    String tempid = all_q_dict.get(qid).getString("id");
                    del_id_list.add(tempid);
                });
            }
        }
        dirty_qids = (List<Integer>) CollectionUtils.subtract(questions.keySet(), old_questions.keySet());
        if (CollectionUtils.isNotEmpty(dirty_qids)) {
            dirty_qids.forEach(qid -> {
                String tempid = questions.get(qid).getString("id");
                del_id_list.add(tempid);
            });
        }

        List<Integer> create_qids = (List<Integer>) CollectionUtils.subtract(old_questions.keySet(), questions.keySet());
        List<Integer> update_qids_temp = (List<Integer>) CollectionUtils.intersection(old_questions.keySet(), questions.keySet());
        List<Integer> update_qids = update_qids_temp.stream().filter(gid -> !old_questions.get(gid).getJSONObject("data").equals(questions.get(gid).getJSONObject("data"))).collect(Collectors.toList());

        if (CollectionUtils.isNotEmpty(del_id_list)) {
            log.info("qdesQuestionMapper.deleteBatchIds(del_id_list={});", del_id_list);
            qdesQuestionMapper.deleteBatchIds(del_id_list);
        }

        List<JSONObject> insertlsit = new ArrayList<>();
        create_qids.forEach(qid -> insertlsit.add(Optional.ofNullable(old_questions.get(qid)).orElse(new JSONObject())));
        createQuestion(insertlsit);

        if (CollectionUtils.isNotEmpty(update_qids)) {
            update_qids.forEach(gid -> {
                JSONObject q = questions.get(gid);
                if (!old_questions.get(gid).getJSONObject("data").equals(q.getJSONObject("data"))) {
                    QdesQuestion up = new QdesQuestion();
                    up.setId(q.getString("id"));
                    up.setData(JSON.toJSONString(old_questions.get(gid).getJSONObject("data")));
                    up.setUpdatedDt(LocalDateTime.now());
                    qdesQuestionMapper.updateById(up);
                }
            });
        }

        // 创建namelist
        boolean copyFlag = false;
        if (StringUtils.isNotEmpty(new_title)) {
            newVersion = qstruct.getVersion();
            copyFlag = true;
        }
        copyNameList(projectId, new_qstruct.getId(), qstruct.getId(), create_qids, newVersion, version, copyFlag);
        copyNameList(projectId, new_qstruct.getId(), qstruct.getId(), update_qids, newVersion, version, copyFlag);
        return qstruct_id;
    }

    private void copyNameList(String project_id, String new_struct_id, String old_struct_id, List<Integer> gids, Integer version, Integer new_version, boolean copy_flag) {
        if (Objects.isNull(new_version)) {
            new_version = 1;
        }
        /*if (Objects.isNull(copy_flag)) {
            copy_flag = false;
        }*/

        for (Integer gid : gids) {
            LambdaQueryWrapper<QdesNameList> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(QdesNameList::getStructId, old_struct_id)
                    .eq(QdesNameList::getGid, gid)
                    .eq(QdesNameList::getVersion, version)
                    .last("limit 1");
            QdesNameList old_name_list = qdesNamelistMapper.selectOne(queryWrapper);
            if (old_name_list == null) {
                continue;
            }

            String struct_id;
            Integer update_version;
            Integer cache_version;
            if (copy_flag) {
                cache_version = new_version;
                struct_id = new_struct_id;
                update_version = version;
            } else {
                new_version = old_name_list.getVersion();
                cache_version = old_name_list.getVersion() + 1;
                struct_id = old_struct_id;
                update_version = old_name_list.getVersion() + 1;
            }

            LambdaUpdateWrapper<QdesNameList> updateWrapper = new LambdaUpdateWrapper<>();
            //构造查询条件-更新
            updateWrapper.set(QdesNameList::getVersion, update_version)
                    .eq(QdesNameList::getId, old_name_list.getId());
            qdesNamelistMapper.update(null, updateWrapper);

            // 新建namelist
            QdesNameList namelist = new QdesNameList();
            namelist.setProjectId(project_id);
            namelist.setStructId(struct_id);
            namelist.setGid(gid);
            namelist.setNtype(old_name_list.getNtype());
            namelist.setEnable(old_name_list.getEnable());
            namelist.setData(old_name_list.getData());
            namelist.setVersion(new_version);
            qdesNamelistMapper.insert(namelist);

            //新版数据 添加缓存信息
            //QDES:6315af123e2c0c000978af07:4:type
            String _key_type = String.format(COPY_NAME_LIST_TYPE_KEY, project_id, cache_version);
            //QDES:632ab5b73e2c0c000878b7c1:3:10:data
            String _key = String.format(COPY_NAME_LIST_DATA_KEY, project_id, cache_version, gid);
            if (StrUtil.isNotBlank(old_name_list.getData())) {
                String name_data = old_name_list.getData();
                if (JSONUtil.isTypeJSONArray(name_data)) {
                    List<String> datalist = JSONObject.parseArray(name_data, String.class);
                    redisService.setAddExpire(_key, 14, TimeUnit.DAYS, datalist.toArray());
                } else {
                    redisService.del(_key);
                }
            }
            JSONObject dic = new JSONObject();
            dic.put("ntype", old_name_list.getNtype());
            dic.put("enable", old_name_list.getEnable());
            redisService.hashSet(_key_type, gid.toString(), dic);
        }
    }

    private void createQuestion(List<JSONObject> insertlsit) {
        if (insertlsit.isEmpty()) {
            return;
        }
        List<QdesQuestion> newQuestions = insertlsit.stream()
                .filter(obk -> !obk.isEmpty())
                .map(it -> {
                    QdesQuestion q = JSONObject.toJavaObject(it, QdesQuestion.class);
                    q.setModified(false);
                    return q;
                })
                .collect(Collectors.toList());
        log.info("createQuestion size={}", newQuestions.size());
        qdesQuestionMapper.insertBatchSomeColumn(newQuestions);
    }

    @Override
    public String genQstructGjs(String pid, QdesQstruct qstruct) throws IOException {
        if (StrUtil.isEmptyIfStr(qstruct.getTemplates()) && StrUtil.isEmptyIfStr(qstruct.getGjs())) {
            return "{}";
        }

        JSONObject retobj = new JSONObject();
        String fileName = "base_js";
        String url = qdesLocalCacheService.get("base_js", k -> {
            if (StrUtil.isEmptyIfStr(staticFile.getBaseJs())) {
                return "";
            } else {
                return staticFile.getBaseJs();
            }
        }).toString();
        if (StrUtil.isEmptyIfStr(url)) {
            retobj.put("base_js", createFileMapData(pid, fileName, url));
        }

        fileName = "base_rem_js";
        url = qdesLocalCacheService.get("base_rem_js", k -> {
            if (StrUtil.isEmptyIfStr(staticFile.getBaseRemJs())) {
                return "";
            } else {
                return staticFile.getBaseRemJs();
            }
        }).toString();
        if (StrUtil.isEmptyIfStr(url)) {
            retobj.put("base_rem_js", createFileMapData(pid, fileName, url));
        }
        return retobj.toJSONString();
    }

    @Override
    public String genQstructUcss(String pid, QdesQstruct qstruct) throws IOException {
        if (StrUtil.isEmptyIfStr(qstruct.getTemplates()) && StrUtil.isEmptyIfStr(qstruct.getGcss())) {
            return "{}";
        }

        JSONObject retobj = new JSONObject();
        String fileName = "base_css";
        //示例https://cem.mezeron.cn/s/static/css/base.css
        String url = qdesLocalCacheService.get("base_css", k -> {
            if (StrUtil.isEmptyIfStr(staticFile.getBaseCss())) {
                return "";
            } else {
                return staticFile.getBaseCss();
            }
        }).toString();
        if (StrUtil.isNotBlank(url)) {
            retobj.put("base_css", createFileMapData(pid, fileName, url));
        }

        fileName = "base_rem_css";
        url = qdesLocalCacheService.get("base_rem_css", k -> {
            if (StrUtil.isEmptyIfStr(staticFile.getBaseCss())) {
                return "";
            } else {
                return staticFile.getBaseRemCss();
            }
        }).toString();
        if (StrUtil.isNotBlank(url)) {
            retobj.put("base_rem_css", createFileMapData(pid, fileName, url));
        }
        return retobj.toJSONString();
    }

    private String createFileMapData(String pid, String fileName, String url) throws IOException {
        if (StringUtils.isEmpty(url)) {
            return "";
        }
        String temppath = ROOT_FILE_PATH + File.separator + StrUtil.replace(OssPathEnum.TEMP_QDES.getPath(), "/", File.separator);
        File file = new File(temppath + fileName);
        long size = HttpUtil.downloadFile(url, file);

        InputStream in = Files.newInputStream(file.toPath());
        String fileId = DigestUtils.md5Hex(in);

        //上传
        //String ossurl = ossService.uploadSimpleFile(file, fileId.substring(5), OssPathEnum.QDES, 0, null, "");
        String ossurl = storageService.upload(file, fileId.substring(5), OssPathEnum.QDES.getPath(), new Dict()).getUrl();

        UploadFileInfo uploadFileInfo = new UploadFileInfo();
        uploadFileInfo.setFileId(fileId);
        uploadFileInfo.setFileSize((int) size);
        uploadFileInfo.setFtype(FileInfoFTypeEnum.FILEINFO_FTYPE_NONE.getValue());
        uploadFileInfo.setPolicy(1);
        uploadFileInfo.setUrl(ossurl);
        uploadFileInfoMapper.insert(uploadFileInfo);

        //只在文件系统中有使用且没有什么实际意义了，暂统一返回xm
        String fileInfoId = uploadFileInfo.getId();
        UploadFileMap uploadFileMap = new UploadFileMap();
        uploadFileMap.setSysId("qdes");
        uploadFileMap.setFileName(fileName);
        uploadFileMap.setFileInfoId(fileInfoId);
        uploadFileMap.setOriginFileInfoId(fileInfoId);
        uploadFileMapMapper.insert(uploadFileMap);
        return ossurl;
    }

    private QdesQstruct createQstruct(QdesQstruct params) {
        String project_id = Optional.ofNullable(params.getProjectId())
                .orElseThrow(() -> new BusinessException("projectID is required"));
        Integer version = params.getVersion();
        if (Objects.nonNull(version) && version > 0) {
            params.setProjectId(project_id);
        } else {
            QdesProject project = Optional.ofNullable(qdesProjectMapper.selectById(project_id))
                    .orElseThrow(() -> new BusinessException(String.format("qdes:project[id=%s] not found", project_id)));
            params.setProjectId(project.getId());
            version = project.getVersion();
            params.setVersion(version);
        }
        String secret = RandomStringUtils.randomAlphanumeric(10);

        LambdaQueryWrapper<QdesQstruct> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(QdesQstruct::getProjectId, project_id)
                .eq(QdesQstruct::getVersion, version)
                .last("limit 1");
        QdesQstruct qstruct = qdesQstructMapper.selectOne(queryWrapper);
        if (Objects.isNull(qstruct)) {
            params.setCsses("[]");
            params.setProjectId(project_id);
            params.setVersion(version);
            params.setSecretKey(secret);
            params.setStatus(QSTRUCT_STATUS_DRAFT.getNumber());
            qdesQstructMapper.insert(params);
            log.info("createQstruct insert");
        } else {
            params.setId(qstruct.getId());
            qdesQstructMapper.updateById(params);
            BeanUtil.copyProperties(params, qstruct, CopyOptions.create().setIgnoreNullValue(true));
            log.info("createQstruct updateById");
        }

        queryWrapper.clear();
        queryWrapper.eq(QdesQstruct::getProjectId, project_id)
                .eq(QdesQstruct::getVersion, version)
                .last("limit 1");
        qstruct = qdesQstructMapper.selectOne(queryWrapper);
        return qstruct;
    }

    private void copyQuotas(String oldId, String newId) {
        LambdaQueryWrapper<QdesQuota> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(QdesQuota::getProjectId, oldId);
        List<QdesQuota> qdesQuotas = qdesQuotaMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(qdesQuotas)) {
            return;
        }
        for (QdesQuota oldQuota : qdesQuotas) {
            // 插入QdesQuota
            QdesQuota quota = new QdesQuota();
            quota.setName(oldQuota.getName());
            quota.setProjectId(newId);
            quota.setQuotaType(oldQuota.getQuotaType());
            quota.setAvaQgidList(oldQuota.getAvaQgidList());
            quota.setRow1(oldQuota.getRow1());
            quota.setRow2(oldQuota.getRow2());
            quota.setCol1(oldQuota.getCol1());
            quota.setCol2(oldQuota.getCol2());
            qdesQuotaMapper.insert(quota);
            String quotaCondList = oldQuota.getQuotaCondList();
            if (StringUtils.isNotEmpty(quotaCondList)) {
                List<String> conditionList = JSONArray.parseArray(quotaCondList, String.class);
                // 调用grpc
                surveyGrpcClient.copySurveyQuotas(conditionList, oldId, newId);
            }
        }
    }

    private void genTempletThumbnail(String libProjectId) {
        QdesLibProject libproject = baseMapper.selectById(libProjectId);
        QdesProject project = qdesProjectMapper.selectById(libproject.getProjectId());
        QdesQstruct qstruct = qdesQstructMapper.selectById(project.getStructId());
        String data = qstruct.getData();
        JSONObject imagDict = null;
        if (StringUtils.isNotEmpty(data)) {
            JSONObject structData = JSONObject.parseObject(data);
            JSONObject descQuestionJson = structData.getJSONObject("desc_question_page_default");
            if (descQuestionJson != null) {
                if (descQuestionJson.getJSONObject(PAGE_HEADER) != null) {
                    imagDict = descQuestionJson.getJSONObject(PAGE_HEADER).getJSONObject("img");
                } else {
                    if (descQuestionJson.getJSONObject(PHONE_PAGE_HEADER) != null) {
                        imagDict = descQuestionJson.getJSONObject(PHONE_PAGE_HEADER).getJSONObject("img");
                    }
                }
            }
        }
        if (imagDict == null) {
            // 获取默认路径
            imagDict = new JSONObject();
            imagDict.put("filePath", "/statics/editor/pc/default/header_bg.png");
        }
        LambdaUpdateWrapper<QdesLibProject> updateWrapper = new LambdaUpdateWrapper<>();
        //构造查询条件-更新
        updateWrapper
                .set(QdesLibProject::getThumbnail, imagDict.getString("filePath"))
                .set(QdesLibProject::getThumbnailFmapId, imagDict.getString("id"))
                .eq(QdesLibProject::getId, libProjectId);
        baseMapper.update(null, updateWrapper);
    }

    private void checkTag(List<TagLibParam> labels) {
        if (labels != null) {
            for (TagLibParam libParam : labels) {
                if (libParam != null) {
                    if (StringUtils.isEmpty(libParam.getTagId())) {
                        log.error("The tagid is null");
                        throw new BusinessException("标签id不能为空");
                    }
                    if (StringUtils.isEmpty(libParam.getTagTitle())) {
                        log.error("The tag_title is null");
                        throw new BusinessException("标签名称不能为空");
                    }
                }
            }
        }
    }

    private void checkHasTitle(String title, String orgId, int status, String id) {
        LambdaQueryWrapper<QdesLibProject> queryWrapper = Wrappers.lambdaQuery();
        //构造查询条件
        queryWrapper.eq(QdesLibProject::getTitle, title)
                .eq(QdesLibProject::getCompanyId, orgId)
                .eq(QdesLibProject::getStatus, status).last("limit 1");
        QdesLibProject qdesLibproject = baseMapper.selectOne(queryWrapper);
        if (qdesLibproject != null) {
            if (id != null && id.equals(qdesLibproject.getId())) {
                return;
            }
            log.error("Error to check has title, title = {}", title);
            throw new BusinessException("名称已存在");
        }
    }

    private void checkTitle(String title) {
        if (StringUtils.isEmpty(title)) {
            log.error("The title is null, title = {}", title);
            throw new BusinessException("模板title为空，请检查。");
        }
        if (title.length() > MAX_LENGTH) {
            log.error("The title is error, title = {}", title);
            throw new BusinessException("模板title长度大于30，请检查。");
        }
        Pattern pattern = Pattern.compile(TITLE_REGEX);
        Matcher matcher = pattern.matcher(title);
        if (matcher.find()) {
            log.error("The title is error, title = {}", title);
            throw new BusinessException("模板title不规范，请检查。");
        }
    }


    private void checkQstructQuestionCid(String structId, boolean needCheck) {
        if (!needCheck) {
            return;
        }
        LambdaQueryWrapper<QdesQuestion> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(QdesQuestion::getStructId, structId);
        List<QdesQuestion> qdesQuestions = qdesQuestionMapper.selectList(queryWrapper);
        for (QdesQuestion qdesQuestion : qdesQuestions) {
            if (StringUtils.isNotEmpty(qdesQuestion.getData())) {
                JSONObject dataJson = JSONObject.parseObject(qdesQuestion.getData());
                if (dataJson.get("cid") != null && dataJson.getString("cid").contains("__")) {
                    log.error("The qdesQuestion cid has __, qdesQuestion = {}", qdesQuestion);
                    throw new BusinessException("题目编号不允许输入双英文下划线。");
                }
            }
        }
    }

    private String getCode(String orgId) {
        int maxCodeNum = qdesProjectMapper.getMaxCodeNum(QdesConstant.CODE_PREFIX.length() + 1, orgId);
        return QdesConstant.CODE_PREFIX + (maxCodeNum + 1);
    }


    private void setTagRefs(String refId, QdesProject newProject, String orgId, String userId) {
        LambdaQueryWrapper<QdesTagRef> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(QdesTagRef::getRefId, refId);
        List<QdesTagRef> qdesTagRefs = qdesTagrefMapper.selectList(queryWrapper);
        if (qdesTagRefs == null) {
            return;
        }
        List<QdesTagRef> newTagrefs = new ArrayList<>();
        qdesTagRefs.forEach(qdesTagref -> {
            QdesTagRef tagref = new QdesTagRef();
            tagref.setTagId(qdesTagref.getTagId());
            tagref.setTagTitle(qdesTagref.getTagTitle());
            tagref.setRefType(TAGREF_REF_TYPE_PROJECT.getNumber());
            tagref.setProjectId(newProject.getId());
            tagref.setCompanyId(orgId);
            tagref.setCreator(userId);
            newTagrefs.add(tagref);
        });
        if (CollectionUtils.isNotEmpty(newTagrefs)) {
            qdesTagrefMapper.insertBatchSomeColumn(newTagrefs);
        }
    }

    private JSONObject copyQstructFile(String fileStr) {
        if (StringUtils.isEmpty(fileStr)) {
            return new JSONObject();
        }
        JSONObject jsonFile = JSONObject.parseObject(fileStr);
        String fileMapId = jsonFile.getString("id");
        if (StringUtils.isNotEmpty(fileMapId)) {
            QdesCopyFile copyFile = uploadFileInfo(fileMapId);
            jsonFile.put("id", copyFile.getFileMapId());
            jsonFile.put("filePath", copyFile.getUrl());
        }
        if (StringUtils.isNotEmpty(jsonFile.getString(UPDATED_DT))) {
            jsonFile.put(UPDATED_DT, DateUtil.getCurrentTime().toString());
        }
        return jsonFile;
    }

    private String copyQstructFileList(String fileStrList) {
        if (StringUtils.isEmpty(fileStrList)) {
            return fileStrList;
        }
        JSONArray jsonFileArray = JSONArray.parseArray(fileStrList);
        JSONArray resultArray = new JSONArray();
        for (int i = 0; i < jsonFileArray.size(); i++) {
            String oneJson = jsonFileArray.getString(i);
            resultArray.add(copyQstructFile(oneJson));
        }
        return resultArray.toJSONString();
    }

    private QdesCopyFile uploadFileInfo(String oid) {
        //创建查询条件对象
        LambdaQueryWrapper<UploadFileMap> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(UploadFileMap::getId, oid).last("limit 1");
        UploadFileMap uploadFileMap = uploadFileMapMapper.selectOne(queryWrapper);
        if (uploadFileMap == null) {
            log.error("The uploadFileMap is null, oid = {}", oid);
            throw new BusinessException("需要拷贝的文件为空，请检查。");
        }
        UploadFileInfo uploadFileInfo = uploadFileInfoMapper.selectById(uploadFileMap.getFileInfoId());
        if (uploadFileInfo == null) {
            log.error("The uploadFileMap is null, oid = {}", oid);
            throw new BusinessException("需要拷贝的文件为空，请检查。");
        }
        // 拷贝fileInfo
        uploadFileInfo.setId(null);
        uploadFileInfo.setCreatedDt(null);
        uploadFileInfo.setUpdatedDt(null);
        uploadFileInfoMapper.insert(uploadFileInfo);
        // 拷贝fileMap
        uploadFileMap.setFileInfoId(uploadFileInfo.getId());
        uploadFileMap.setOriginFileInfoId(uploadFileInfo.getId());
        uploadFileMap.setId(null);
        uploadFileMap.setCreatedDt(null);
        uploadFileMap.setUpdatedDt(null);
        uploadFileMapMapper.insert(uploadFileMap);
        QdesCopyFile copyFile = new QdesCopyFile();
        copyFile.setFileMapId(uploadFileMap.getId());
        copyFile.setUrl(uploadFileInfo.getUrl());
        return copyFile;
    }

    /**
     * 默认自定义属性
     *
     * @return 数据
     */
    @NotNull
    private JSONObject getDefaultCustomAttr() {
        //String attr = ResourceUtil.readUtf8Str(CUSTOM_ATTR_FILE_PATH);
        //return JSON.parseObject(attr);

        String attr = ResourceUtil.readUtf8Str(CUSTOM_ATTR_FILE_PATH);
        //String urlPrefix = StrUtil.removeSuffix(ossService.getCdn() + ossService.getFilePrefix(), "/");
        String urlPrefix = StrUtil.removeSuffix(storageService.getPropertiesValueBy(null, "domain") + storageService.getPropertiesValueBy(null, "base-path"), "/");
        return JSON.parseObject(String.format(attr, urlPrefix, urlPrefix));
        /*JSONObject customAttr = new JSONObject();
        customAttr.put("ipOnce", "false");
        customAttr.put("devOnce", "false");
        customAttr.put("time_flag", "false");
        customAttr.put("nums_flag", "false");
        customAttr.put("begin_time", "");
        customAttr.put("end_time", "");
        customAttr.put("sample_num", "");
        customAttr.put("wxOnce", "false"); // 微信 答题次数限制开启
        customAttr.put("wxOnce_time", "90_days"); // 微信 控制答题的时间范围 只能、每小时、每天。。。 同ip、设备限制相同
        customAttr.put("wxOnce_num", 1); // 微信 控制答题次数
        customAttr.put("wxOnly", "false"); // 只能通过微信答题
        customAttr.put("wxShare", "true"); // 允许微信右上角分享
        customAttr.put("wxShare_img", "https://rs0.bestcem.cn/prod/rs/survey/logo0515.jpg"); // 自定义微信分享图标
        customAttr.put("wxShare_title", ""); // 自定义微信分享标题
        customAttr.put("wxCardShare_bg_img", ""); // 问卷分享卡片背景图
        customAttr.put("wxCardShare_title", ""); // 问卷分享卡片标题
        customAttr.put("wxShare_desc", "感谢您能抽出几分钟时间来填写以下内容，现在我们就马上开始吧！"); // 自定义微信分享描述
        customAttr.put("wxInfo_record", "false"); // 授权给公众号
        customAttr.put("wxInfo_collection", 0); // 微信信息收集方式
        customAttr.put("wxAuth_CEM", 0); // 通过倍市得官方微信服务号来收集信息或投放问卷
        customAttr.put("wxAuth_Custom", ""); // 我的微信认证服务号，最多授权四个
        customAttr.put("wxShare_qrcode_img", ""); // 微信分享二维码图片
        customAttr.put("wxShare_qrcode_img_id", ""); // 微信分享二维码id
        return customAttr;*/
    }

    @Override
    public ServiceResult<?> getOperableProjectList(Integer permission, Integer page, Integer rowsPerPage, String title, Integer status, String userId) {
        if (Objects.isNull(page) && Objects.isNull(rowsPerPage)) {
            List<OperableProjectVo> operableProjectList = qdesProjectMapper.getOperableProjectList(permission, userId);
            if (operableProjectList.size() > 0) {
                List<String> creator_ids = operableProjectList.stream().map(OperableProjectVo::getCreatorId).collect(Collectors.toList());
                Map<String, String> creatorMap = new HashMap<>();
                creator_ids.forEach(v -> {
                    UserDto user = userGrpcClient.getUser(v);
                    creatorMap.put(v, Optional.ofNullable(user.getName()).orElse(""));
                });
                operableProjectList.forEach(v -> {
                    v.setCreator(creatorMap.get(v.getUserId()));
                    v.setFinishedCount(getFinishedRspdCount(v.getProjectId(), RESPONDENT_DATA_STATUS_FORMAL.getNumber(), RESPONDENT_STATUS_FINISH.getNumber()));
                });
            }
            return ServiceResult.success(operableProjectList);
        }
        Long count = qdesProjectMapper.countOperableProjectList(userId, permission, title, status);
        if (count <= 0) {
            return ServiceResult.success(PageUtil.getResultPage(page, count, Collections.emptyList()));
        }
        page = Optional.ofNullable(page).orElse(1);
        rowsPerPage = Optional.ofNullable(rowsPerPage).orElse(10);
        List<ProjectEntryVo> list = qdesProjectMapper.pageOperableProjectList(userId, permission, (page - 1) * rowsPerPage, rowsPerPage, title, status);
        if (list.size() > 0) {
            List<String> creator_ids = list.stream().map(ProjectEntryVo::getCreatorId).collect(Collectors.toList());
            Map<String, String> creatorMap = new HashMap<>();
            creator_ids.forEach(v -> {
                UserDto user = userGrpcClient.getUser(v);
                creatorMap.put(v, Optional.ofNullable(user.getName()).orElse(""));
            });
            list.forEach(v -> {
                v.setCreator(creatorMap.get(v.getUserId()));
                v.setFinishedCount(getFinishedRspdCount(v.getProjectId(), RESPONDENT_DATA_STATUS_FORMAL.getNumber(), RESPONDENT_STATUS_FINISH.getNumber()));
                v.setProjData(qdesProjectMapper.selectById(v.getProjectId()));
            });
        }
        return ServiceResult.success(PageUtil.getResultPage(page, count, list));
    }

    private int getFinishedRspdCount(String projectId, Integer dataStatus, Integer status) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("project_id", projectId);
        jsonObject.put("data_status", dataStatus);
        jsonObject.put("status", status);
        List<SurveyRespondentDto> dtoList = surveyGrpcClient.listRespondent(jsonObject);
        return dtoList.size();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer projectTop(String id, Boolean setTop, String userId) {
        LambdaQueryWrapper<QdesProjectEntry> wrapper = new LambdaQueryWrapper<QdesProjectEntry>()
                .eq(QdesProjectEntry::getProjectId, id)
                .eq(QdesProjectEntry::getUserId, userId);
        QdesProjectEntry qdesProjectentry = new QdesProjectEntry();
        qdesProjectentry.setTopOrder(setTop ? serviceSequenceService.getSequence("top_order_" + userId, 1) : 0);
        qdesProjectentry.setUpdatedId(userId);
        return qdesProjectentryMapper.update(qdesProjectentry, wrapper);
    }

    @Data
    public static class QdesCopyFile {
        /**
         * 图片id
         */
        private String fileMapId;

        /**
         * url
         */
        private String url;
    }
}